package nio.reactor.single;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Iterator;
import java.util.Set;

/**
 * User: Administrator
 * Date: 2021/4/13
 * Time: 15:34
 * Description:
 */
public class EchoServerReactor implements Runnable {
    public static void main(String[] args) throws IOException {
        new Thread(new EchoServerReactor()).start();
    }


    Selector selector;
    ServerSocketChannel serverSocketChannel;

    public EchoServerReactor() throws IOException {
        //打开选择器 打开通道 监听端口
        selector = Selector.open();
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.bind(new InetSocketAddress(9088));
        //注册accept事件
        SelectionKey key = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        //将连接处理器作为附件，绑定到选择键
        key.attach(new AcceptorHandler());
    }

    @Override
    public void run() {
        while (!Thread.interrupted()) {
            try {
                selector.select();
                Set<SelectionKey> keys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = keys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey next = iterator.next();
                    dispatch(next);
                }
                keys.clear();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    void dispatch(SelectionKey key) {
        Runnable handler = (Runnable) key.attachment();
        if (handler != null) {
            handler.run();
        }
    }

    class AcceptorHandler implements Runnable {

        @Override
        public void run() {
            try {
                SocketChannel channel = serverSocketChannel.accept();
                if (channel != null) {
                    //new EchoHandler(selector, channel);
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    Charset charset = Charset.forName("UTF-8");
                    CharsetDecoder decoder = charset.newDecoder();
                    String s1 = decoder.decode(byteBuffer.asReadOnlyBuffer()).toString();
                    System.out.println(s1);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    class EchoHandler implements Runnable {
        final SocketChannel channel;
        final SelectionKey key;
        final ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        static final int rec = 0, send = 1;
        int state = rec;

        EchoHandler(Selector selector, SocketChannel channel) throws IOException {
            this.channel = channel;
            this.channel.configureBlocking(false);
            key = this.channel.register(selector, 0);
            key.attach(this);
            key.interestOps(SelectionKey.OP_READ);
            selector.wakeup();
        }

        @Override
        public void run() {
            try {
                if (state == send) {
                    channel.write(byteBuffer);
                    byteBuffer.clear();
                    key.interestOps(SelectionKey.OP_READ);
                    state = rec;
                } else {
                    while(channel.read(byteBuffer) > 0) {
                        Charset charset = Charset.forName("UTF-8");
                        CharsetDecoder decoder = charset.newDecoder();
                        String s1 = decoder.decode(byteBuffer.asReadOnlyBuffer()).toString();
                        System.out.println(s1);
                    }
                    byteBuffer.flip();
                    key.interestOps(SelectionKey.OP_WRITE);
                    state = send;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
